package Account

import (
	"Identity/common"
	"crypto/ecdsa"
	"crypto/rand"
	"encoding/hex"
	"github.com/ethereum/go-ethereum/crypto"
	"log"
)

/*
私钥、公钥和地址是如何生成的？

以太坊地址生成的流程也是：私钥 -> 公钥 -> 地址。因此地址的生成需要三步：

生成一个随机的私钥（32字节）
通过私钥生成公钥（64字节）
通过公钥得到地址（20字节）
私钥推导公钥

私钥是一组64位的16进制字符，通过私钥我们能够访问一个账户。以太坊的私钥生成是通过secp256k1椭圆曲线算法生成的，secp256k1是一个椭圆曲线算法，同比特币。



公钥推导地址

和比特币相比，在私钥生成公钥这一步其实是一样的，区别在公钥推导地址第一部分，以太坊中非圧缩型公钥的处理就简单粗暴很多了，下图为Public Key生成Address的过程。



以太坊地址生成的过程

第一步：私钥 (private key)

　　伪随机数产生的256bit私钥示例(256bit  16进制32字节)

　　18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725

第二步：公钥 (public key)

　　	   1. 采用椭圆曲线数字签名算法ECDSA-secp256k1将私钥（32字节）映射成公钥（65字节）（前缀04+X公钥+Y公钥）：

　　04
　　50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352
　　2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6

       2. 拿公钥（非压缩公钥）来hash，计算公钥的 Keccak-256 哈希值（32bytes）：

　　fc12ad814631ba689f7abe671016f75c54c607f082ae6b0881fac0abeda21781

       3. 取上一步结果取后20bytes即以太坊地址：

　　1016f75c54c607f082ae6b0881fac0abeda21781

第三步：地址 (address)

　　0x1016f75c54c607f082ae6b0881fac0abeda21781
*/

//校验和长度
const AddressCheckSumLen = 4
var PubKeyPrefix,_ = hex.DecodeString("04")

//钱包基本结构
type Wallet struct {
	//1.私钥
	PrivateKey ecdsa.PrivateKey
	//2。公钥
	PublicKey []byte
}

func NewWallet()*Wallet{

	pri,pub := NewKeyPair()
	//公钥--私钥赋值
	return &Wallet{PrivateKey:pri,PublicKey:pub}
}

func (w * Wallet)PubKeyLen() int {
	return len(w.PublicKey)
}

func NewKeyPair() (ecdsa.PrivateKey,[]byte){
	//1.获取一个椭圆
	curve  := crypto.S256()
	//2.通过椭圆相关算法生成私钥
	/* 之后把 rand.Reader 换成图片信息的io.Reader */
	privateKey,err := ecdsa.GenerateKey(curve,rand.Reader)
	if nil != err{
		log.Panicf("ecdsa generate private key failed!%v\n",err)
	}
	//3。通过私钥生成公钥
	pubKey := EcdsaPub2BytePub(privateKey.PublicKey)

	return *privateKey,pubKey
}

func EcdsaPub2BytePub(epub ecdsa.PublicKey) []byte  {
	//3。通过私钥生成公钥
	pubKey := append(epub.X.Bytes(),epub.Y.Bytes()...)
	///* 添加04 前缀*/
	//pubKey = append(PubKeyPrefix,pubKey...)

	return pubKey
}

func GenerateAddress( pubkey []byte )  common.Address {
	keccak256Hash := crypto.Keccak256(pubkey)
	/*取后20字节*/
	address := keccak256Hash[len(keccak256Hash)-common.AddressLength:]
	return common.BytesToAddress(address)
}

/* 之后升级成可生成多个账户地址 */
//生成地址
func (w * Wallet)GenerateAddress( )  []byte {
	keccak256Hash := crypto.Keccak256(w.PublicKey)
	/*取后20字节*/
	address := keccak256Hash[len(keccak256Hash)-common.AddressLength:]
	return address
}
/* 创建身份账户 */
func (w * Wallet)CreateIdentityAccount() *IdentityAccount {
	return &IdentityAccount{
		Address: w.GenerateAddress(),
	}
}


